//#########################################################################
//                              WackiEngie
//           Copyright (c) 2014 Marc Wacker. All rights reserved.
//#########################################################################

#pragma once

#include "WeUtilPrerequisites.h"

namespace WackiEngine
{
	// singleton interface used for all major modules of the engine that need to be accessible from anywhere
	// (ex.: application, input etc...)
	template<class T>
	class Module
	{
	public:

		static T* instancePtr()
		{
			if(!isStarted())
				throw "Trying to access a module that hasn't been started up."; // TODO: add some real exception handling

			if(isDestroyed())
				throw "Trying to access destroyed moule."; // TODO: add some real exception handling

			return _instance();
		}

		static T& instance()
		{
			return *instancePtr();
		}
		
		template<class ...Args>
		static void startUp(Args&&... args)
		{
			if(isStarted() && !isDestroyed())
				throw "can't start an already started module";

			_instance() = new T(std::forward<Args>(args)...);
			isStarted() = true;

			((Module*)_instance())->onStartUp();
		}

		static void shutDown()
		{
			if(!isStarted() && isDestroyed())
				throw "can't shut down an already shut down module.";

			((Module*)_instance())->onShutDown();
			delete _instance();
			_instance() = nullptr;
			isStarted() = false;
		}


	protected:
		Module()
		{ }

		virtual ~Module()
		{ 
			_instance() = nullptr;
			isDestroyed() = true;
		}

		// don't allow copying
		Module(const Module&) { }
		Module& operator=(const Module&) { return *this; }

		// virtual functions to know when a module is started and shut down
		virtual void onStartUp() {}
		virtual void onShutDown() {}

	private:

		static T*&	_instance()
		{
			static T* inst = nullptr;
			return inst;
		}

		static bool& isStarted()
		{
			static bool isStarted = false;
			return isStarted;
		}

		static bool& isDestroyed()
		{
			static bool isDestroyed = false;
			return isDestroyed;
		}
	};

}